package com.agileboot.attendance.domain.settlement.service.impl;

import com.agileboot.attendance.domain.record.dto.AttendanceStatisticsDTO;
import com.agileboot.attendance.domain.record.service.AttendanceStatisticsService;
import com.agileboot.attendance.domain.settlement.dto.*;
import com.agileboot.attendance.domain.settlement.entity.AttSettlementDetailEntity;
import com.agileboot.attendance.domain.settlement.entity.AttSettlementEntity;
import com.agileboot.attendance.domain.settlement.enums.SettlementErrorCode;
import com.agileboot.attendance.domain.settlement.enums.SettlementPeriodType;
import com.agileboot.attendance.domain.settlement.enums.SettlementStatus;
import com.agileboot.attendance.domain.settlement.mapper.AttSettlementDetailMapper;
import com.agileboot.attendance.domain.settlement.mapper.AttSettlementMapper;
import com.agileboot.attendance.domain.settlement.service.SettlementApproveService;
import com.agileboot.attendance.domain.settlement.service.SettlementService;
import com.agileboot.attendance.domain.settlement.utils.SettlementBillNoGenerator;
import com.agileboot.attendance.domain.order.entity.AttOrderEntity;
import com.agileboot.attendance.domain.order.db.AttOrderService;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.exception.ApiException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 结算单服务实现类
 */
@Service
@RequiredArgsConstructor
public class SettlementServiceImpl implements SettlementService {

    private final AttSettlementMapper settlementMapper;
    private final AttSettlementDetailMapper settlementDetailMapper;
    private final SettlementBillNoGenerator billNoGenerator;
    private final AttendanceStatisticsService attendanceStatisticsService;
    private final SettlementApproveService approveService;
    private final AttOrderService orderService;

    @Override
    public PageDTO<SettlementBillDTO> list(SettlementBillQuery query) {
        Page<AttSettlementEntity> page = new Page<>(query.getPageNum(), query.getPageSize());
        
        // 使用Mapper中的方法查询结算单列表
        List<AttSettlementEntity> list = settlementMapper.selectSettlementList(query);
        
        // 手动设置分页信息
        page.setRecords(list);
        page.setTotal(list.size());
        page.setCurrent(query.getPageNum());
        page.setSize(query.getPageSize());
        
        List<SettlementBillDTO> dtoList = list.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        // 使用已有的PageDTO构造函数
        PageDTO<SettlementBillDTO> pageDTO = new PageDTO<>(dtoList, page.getTotal());
        pageDTO.setPageNum(page.getCurrent());
        pageDTO.setPageSize(page.getSize());
        
        return pageDTO;
    }

    @Override
    public SettlementBillDTO getInfo(Long id) {
        // 查询结算单基本信息
        AttSettlementEntity entity = settlementMapper.selectById(id);
        if (entity == null) {
            throw new ApiException(SettlementErrorCode.SETTLEMENT_NOT_FOUND);
        }
        
        // 基础数据转换
        SettlementBillDTO dto = SettlementBillDTO.fromEntity(entity);
        
        // 查询订单和关联信息
        if (entity.getOrderId() != null) {
            // 获取订单信息
            AttOrderEntity order = orderService.getById(entity.getOrderId());
            if (order != null) {
                dto.setOrderNo(order.getOrderCode());
                dto.setProjectId(order.getProjectId());
                
                // 获取项目信息
                if (order.getProjectId() != null) {
                    // 方式1: 使用XML中关联查询已经有的项目名称（如果存在）
                    // 方式2: 使用ProjectService手动查询
                    // 这里我们先尝试从订单关联查询的结果中获取
                    if (dto.getProjectName() == null && order.getProjectId() != null) {
                        // 需要手动查询项目获取名称
                        String projectName = getProjectNameById(order.getProjectId());
                        dto.setProjectName(projectName);
                    }
                }
                
                // 设置结算周期
                if (order.getSettlementCycle() != null) {
                    SettlementPeriodType periodType = getSettlementPeriodType(order.getSettlementCycle());
                    if (periodType != null) {
                        dto.setPeriodType(periodType);
                        dto.setPeriodTypeDesc(periodType.getLabel());
                    }
                }
            }
        }
        
        // 查询供应商信息
        if (entity.getSupplierId() != null && dto.getSupplierName() == null) {
            dto.setSupplierName(getSupplierNameById(entity.getSupplierId()));
        }
        
        // 查询结算明细
        List<AttSettlementDetailEntity> details = settlementDetailMapper.selectDetailList(id);
        
        // 处理明细，计算金额
        List<SettlementBillDetailDTO> detailDTOs = details.stream()
                .map(detail -> {
                    SettlementBillDetailDTO detailDTO = SettlementBillDetailDTO.fromEntity(detail);
                    
                    // 计算出勤金额（如果为空）
                    if (detailDTO.getAttendanceAmount() == null && detailDTO.getDailyRate() != null && detailDTO.getAttendanceDays() != null) {
                        BigDecimal attendanceAmount = detailDTO.getDailyRate()
                                .multiply(BigDecimal.valueOf(detailDTO.getAttendanceDays()));
                        detailDTO.setAttendanceAmount(attendanceAmount);
                    }
                    
                    // 计算加班金额（如果为空）
                    if (detailDTO.getOvertimeAmount() == null && detailDTO.getOvertimeRate() != null && detailDTO.getOvertimeHours() != null) {
                        BigDecimal overtimeAmount = detailDTO.getOvertimeRate()
                                .multiply(detailDTO.getOvertimeHours());
                        detailDTO.setOvertimeAmount(overtimeAmount);
                    }
                    
                    return detailDTO;
                })
                .collect(Collectors.toList());
        
        dto.setDetails(detailDTOs);
        
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long add(SettlementBillRequest request) {
        // 转换为实体
        AttSettlementEntity entity = new AttSettlementEntity();
        BeanUtils.copyProperties(request, entity);

        // 设置结算单号和状态
        entity.setBillNo(billNoGenerator.generate());
        entity.setStatus(SettlementStatus.DRAFT);

        // 保存结算单
        settlementMapper.insert(entity);

        // 计算并保存结算明细
        calculateAndSaveDetails(entity, request.getPersonnelIds());

        return entity.getSettlementId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(SettlementBillRequest request) {
        // 检查结算单是否存在
        LambdaQueryWrapper<AttSettlementEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AttSettlementEntity::getSettlementId, request.getId());
        AttSettlementEntity entity = settlementMapper.selectOne(wrapper);
        if (entity == null) {
            throw new ApiException(SettlementErrorCode.SETTLEMENT_NOT_FOUND);
        }

        // 只有草稿状态的结算单才能修改
        if (entity.getStatus() != SettlementStatus.DRAFT) {
            throw new ApiException(SettlementErrorCode.SETTLEMENT_DRAFT_ONLY, "修改");
        }

        // 更新结算单
        BeanUtils.copyProperties(request, entity);
        settlementMapper.updateById(entity);

        // 删除原有明细
        LambdaQueryWrapper<AttSettlementDetailEntity> detailWrapper = new LambdaQueryWrapper<>();
        detailWrapper.eq(AttSettlementDetailEntity::getSettlementId, entity.getSettlementId());
        settlementDetailMapper.delete(detailWrapper);

        // 重新计算并保存结算明细
        calculateAndSaveDetails(entity, request.getPersonnelIds());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long[] ids) {
        for (Long id : ids) {
            // 检查结算单是否存在
            LambdaQueryWrapper<AttSettlementEntity> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(AttSettlementEntity::getSettlementId, id);
            AttSettlementEntity entity = settlementMapper.selectOne(wrapper);
            if (entity == null) {
                continue;
            }

            // 只有草稿状态的结算单才能删除
            if (entity.getStatus() != SettlementStatus.DRAFT) {
                throw new ApiException(SettlementErrorCode.SETTLEMENT_DRAFT_ONLY, "删除");
            }

            // 删除结算单明细
            LambdaQueryWrapper<AttSettlementDetailEntity> detailWrapper = new LambdaQueryWrapper<>();
            detailWrapper.eq(AttSettlementDetailEntity::getSettlementId, id);
            settlementDetailMapper.delete(detailWrapper);

            // 删除结算单
            LambdaQueryWrapper<AttSettlementEntity> deleteWrapper = new LambdaQueryWrapper<>();
            deleteWrapper.eq(AttSettlementEntity::getSettlementId, id);
            settlementMapper.delete(deleteWrapper);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitReview(Long id) {
        approveService.submit(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approve(SettlementApproveDTO approveDTO) {
        approveService.approve(approveDTO);
    }

    @Override
    public List<Map<String, String>> getBillNoOptions() {
        // 查询所有未删除的结算单号
        LambdaQueryWrapper<AttSettlementEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AttSettlementEntity::getDeleted, false)
               .select(AttSettlementEntity::getBillNo);
        
        // 查询结果转为选项格式
        List<AttSettlementEntity> list = settlementMapper.selectList(wrapper);
        return list.stream()
                .map(entity -> {
                    Map<String, String> option = new HashMap<>();
                    option.put("billNo", entity.getBillNo());
                    return option;
                })
                .collect(Collectors.toList());
    }

    /**
     * 转换为DTO
     */
    private SettlementBillDTO convertToDTO(AttSettlementEntity entity) {
        SettlementBillDTO dto = SettlementBillDTO.fromEntity(entity);

        // 设置结算周期类型
        if (entity.getSettlementCycle() != null) {
            String cycleValue = entity.getSettlementCycle();
            try {
                int cycleInt = Integer.parseInt(cycleValue);
                // 根据周期值设置枚举类型
                for (SettlementPeriodType type : SettlementPeriodType.values()) {
                    if (type.getValue() == cycleInt) {
                        dto.setPeriodType(type);
                        dto.setPeriodTypeDesc(type.getLabel());
                        break;
                    }
                }
            } catch (NumberFormatException e) {
                // 忽略转换错误
            }
        }
        
        // Status已经在fromEntity方法中正确设置，无需在此处理

        return dto;
    }

    /**
     * 计算并保存结算明细
     */
    private void calculateAndSaveDetails(AttSettlementEntity settlement, List<Long> personnelIds) {
        if (personnelIds == null || personnelIds.isEmpty()) {
            return;
        }

        // 获取考勤统计数据
        List<AttendanceStatisticsDTO> statisticsList = attendanceStatisticsService.getAttendanceStatistics(
                personnelIds, settlement.getStartDate(), settlement.getEndDate());

        // 计算并保存明细
        BigDecimal totalAmount = BigDecimal.ZERO;
        for (AttendanceStatisticsDTO statistics : statisticsList) {
            AttSettlementDetailEntity detail = new AttSettlementDetailEntity();
            detail.setSettlementId(settlement.getSettlementId());
            detail.setPersonnelId(statistics.getPersonnelId());
            detail.setPersonnelName(statistics.getPersonnelName());
            detail.setAttendanceDays(statistics.getAttendanceDays());
            detail.setOvertimeHours(statistics.getOvertimeHours());
            detail.setDailyRate(statistics.getDailyRate());
            detail.setOvertimeRate(statistics.getOvertimeRate());

            // 计算金额
            BigDecimal attendanceAmount = statistics.getDailyRate()
                    .multiply(BigDecimal.valueOf(statistics.getAttendanceDays()));
            BigDecimal overtimeAmount = statistics.getOvertimeRate()
                    .multiply(statistics.getOvertimeHours());
            BigDecimal detailTotal = attendanceAmount.add(overtimeAmount);

            detail.setAttendanceAmount(attendanceAmount);
            detail.setOvertimeAmount(overtimeAmount);
            detail.setTotalAmount(detailTotal);

            // 保存明细
            settlementDetailMapper.insert(detail);

            // 累加总金额
            totalAmount = totalAmount.add(detailTotal);
        }

        // 更新结算单总金额
        settlement.setTotalAmount(totalAmount);
        settlementMapper.updateById(settlement);
    }

    /**
     * 根据ID获取项目名称
     */
    private String getProjectNameById(Long projectId) {
        // 实际项目中应该调用项目服务获取名称
        // 这里仅做示例，真实环境需要替换为实际逻辑
        Map<Long, String> projectCache = new HashMap<>();
        projectCache.put(101L, "沪联金融核心系统开发");
        projectCache.put(102L, "金融风控平台建设");
        projectCache.put(103L, "数据中台建设项目");
        
        return projectCache.getOrDefault(projectId, "未知项目");
    }

    /**
     * 根据ID获取供应商名称
     */
    private String getSupplierNameById(Long supplierId) {
        // 实际项目中应该调用供应商服务获取名称
        // 这里仅做示例，真实环境需要替换为实际逻辑
        Map<Long, String> supplierCache = new HashMap<>();
        supplierCache.put(104L, "上海科技信息有限公司");
        supplierCache.put(105L, "江苏软件开发有限公司");
        supplierCache.put(106L, "北京云计算服务有限公司");
        
        return supplierCache.getOrDefault(supplierId, "未知供应商");
    }

    /**
     * 获取结算周期类型
     */
    private SettlementPeriodType getSettlementPeriodType(String cycleValue) {
        try {
            // 尝试直接映射枚举
            return SettlementPeriodType.valueOf(cycleValue);
        } catch (IllegalArgumentException e) {
            // 尝试转换数字
            try {
                int cycleInt = Integer.parseInt(cycleValue);
                for (SettlementPeriodType type : SettlementPeriodType.values()) {
                    if (type.getValue() == cycleInt) {
                        return type;
                    }
                }
            } catch (NumberFormatException ex) {
                // 忽略转换错误
            }
        }
        return null;
    }
}
